import axios, { type AxiosRequestConfig, type AxiosResponse } from 'axios';
import type { AxiosConfig } from '../types';
import { getToken } from './storage';
import qs from 'qs';

// 初始化axios配置
let processing: boolean; // 处理中...
export function initAxios(options: AxiosConfig, message: any): void {
  axios.defaults.withCredentials = true; // 跨域请求时发送Cookie
  axios.defaults = Object.assign(axios.defaults, options.axiosDefaults); // 合并默认配置
  // 请求拦截器
  axios.interceptors.request.use(
    (req) => {
      req.headers.Authorization = getToken();
      return req;
    },
    (err) => Promise.reject(err)
  );
  // 响应拦截器
  axios.interceptors.response.use(
    (res) => {
      // 响应成功（0/false/undefind）
      if (!res.data.code) {
        return res;
      }
      // 短Token过期，刷新Token
      if ((options.refreshTokenStatusCodes || [402]).includes(res.data.code)) {
        return refreshToken(options, res);
      }
      // Token不存在或Token无效
      if ((options.unauthorizedStatusCodes || [401]).includes(res.data.code)) {
        if (!processing) {
          processing = true;
          setTimeout(() => {
            processing = false;
          }, 1500);
          options.unauthorizedHandler();
        }
      } else {
        message && message.error(res.data.msg || 'Axios Error');
      }
      return Promise.reject(res);
    },
    (err) => {
      // 短Token过期，刷新token
      if ((options.refreshTokenStatusCodes || [402]).includes(err.status)) {
        return refreshToken(options, err.response);
      }
      // Token不存在或Token无效
      if ((options.unauthorizedStatusCodes || [401]).includes(err.status)) {
        if (!processing) {
          processing = true;
          setTimeout(() => {
            processing = false;
          }, 1500);
          options.unauthorizedHandler();
        }
      } else {
        message && message.error(err.response.data.msg || err.message || 'Axios Error');
      }
      return Promise.reject(err.response);
    }
  );
}

// 过期刷新token
let pending: boolean;
let pendingList: any[] = [];
function refreshToken(options: AxiosConfig, response: AxiosResponse): Promise<any> {
  console.log('请求刷新Token');
  if (!pending) {
    pending = true;
    // 请求刷新Token
    options.refreshTokenRequest!().then(() => {
      console.log('刷新Token成功');
      pending = false;
      pendingList.forEach((cb) => cb()); // 释放所有等待
      pendingList = [];
    });
  }
  // 存储所有等待
  return new Promise((resolve) => {
    pendingList.push(() => {
      console.log('释放等待请求');
      axios.request(response.config).then((res) => {
        resolve(res);
      });
    });
  });
}

/**
 * @description GET请求
 * @param {string} url 请求地址
 * @param {object} data 请求参数
 * @param {string} config 请求配置
 * @return {Promise} 返回结果
 */
export function getJSON(url: string, data?: any, config?: AxiosRequestConfig<any>): Promise<any> {
  return new Promise((resolve, reject) => {
    axios
      .get(url, {
        ...config,
        params: data,
        paramsSerializer: (params) => {
          return qs.stringify(params, { arrayFormat: 'repeat' });
        },
      })
      .then(
        (res) => {
          resolve(res.data);
        },
        (err) => {
          reject(err);
        }
      );
  });
}

/**
 * @description POST请求
 * @param {string} url 请求地址
 * @param {object} data 请求参数
 * @param {string} config 请求配置
 * @return {Promise} 返回结果
 */
export function postJSON(url: string, data?: any, config?: AxiosRequestConfig<any>): Promise<any> {
  return new Promise((resolve, reject) => {
    axios.post(url, data, config).then(
      (res) => {
        resolve(res.data);
      },
      (err) => {
        reject(err);
      }
    );
  });
}

/**
 * @description PUT请求
 * @param {string} url 请求地址
 * @param {object} data 请求参数
 * @param {string} config 请求配置
 * @return {Promise} 返回结果
 */
export function putJSON(url: string, data?: any, config?: AxiosRequestConfig<any>): Promise<any> {
  return new Promise((resolve, reject) => {
    axios.put(url, data, config).then(
      (res) => {
        resolve(res.data);
      },
      (err) => {
        reject(err);
      }
    );
  });
}

/**
 * @description DELETE请求
 * @param {string} url 请求地址
 * @param {object} data 请求参数
 * @param {string} config 请求配置
 * @return {Promise} 返回结果
 */
export function deleteJSON(url: string, data?: any, config?: AxiosRequestConfig<any>): Promise<any> {
  return new Promise((resolve, reject) => {
    axios.delete(url, { ...config, data: data }).then(
      (res) => {
        resolve(res.data);
      },
      (err) => {
        reject(err);
      }
    );
  });
}

/**
 * @description 文件上传
 * @param {string} url 请求地址
 * @param {any} file 上传file文件/表单change事件
 * @param {object} params 附加参数
 * @param {function} onProgress 上传进度监听事件
 * @return {Promise} 返回结果
 */
export function uploadFile(url: string, file: any, params: object, onProgress?: (e: any) => void): Promise<any> {
  const formData = new FormData(); // 创建form对象
  if (Array.isArray(file)) {
    for (let i = 0; i < file.length; i++) {
      formData.append('files', file[i]);
    }
  } else {
    formData.append('file', file.target && file.target.files ? file.target.files[0] : file);
  }
  // 附加参数
  Object.keys(params).forEach((key: string) => {
    formData.append(key, params[key as keyof typeof params]);
  });
  // 上传配置
  const config = {
    headers: { 'Content-Type': 'multipart/form-data' },
    onUploadProgress: onProgress,
  };
  return new Promise((resolve, reject) => {
    axios.post(url, formData, config).then(
      (res) => {
        resolve(res.data);
      },
      (err) => {
        reject(err);
      }
    );
  });
}

/**
 * @description 文件下载
 * @param {string} url 请求地址
 * @param {object} data 请求参数
 * @param {string} method 请求方式
 * @return {Promise} 返回文件
 */
export function downloadFile(url: string, data?: any, method = 'get'): Promise<any> {
  return new Promise((resolve, reject) => {
    axios({
      method: method,
      url: url,
      params: data, // GET有效
      data: data, // POST有效
      responseType: 'blob',
      headers: {
        'Content-Type': 'application/json',
      },
    }).then(
      (res) => {
        const filename = res.headers['content-disposition'].toString().replace(/attachment;\s*filename=/g, '');
        const blob = new Blob([res.data]);
        const elink = document.createElement('a');
        elink.download = decodeURIComponent(filename); // 文件名解码
        elink.href = URL.createObjectURL(blob);
        elink.style.display = 'none';
        document.body.appendChild(elink);
        elink.click();
        URL.revokeObjectURL(elink.href); // 释放URL对象
        document.body.removeChild(elink);
        resolve(decodeURIComponent(filename));
      },
      (err) => {
        reject(err);
      }
    );
  });
}
